package com.esd.device.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.esd.common.pojo.PageResult;
import com.esd.common.pojo.R;
import com.esd.device.entity.EsdLog;
import com.esd.device.entity.Line;
import com.esd.device.entity.dto.AnalysisSearch;
import com.esd.device.entity.dto.EsdLogSearch;
import com.esd.device.mapper.DeviceChannelMapper;
import com.esd.device.mapper.EsdLogMapper;
import com.esd.device.mapper.LineMapper;
import com.esd.device.service.EsdLogService;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class EsdLogServiceImpl extends ServiceImpl<EsdLogMapper, EsdLog> implements EsdLogService {

    @Override
    public R logSearch(EsdLogSearch esdLogSearch) {
        Page<EsdLog> deviceLogPage = new Page<>(esdLogSearch.getCurrentPage(), esdLogSearch.getPageSize());
        LambdaQueryWrapper<EsdLog> query = new LambdaQueryWrapper<>();
        if (esdLogSearch.getStationId() != null && !esdLogSearch.getStationId().isEmpty()){
            query.like(EsdLog::getStationId, esdLogSearch.getStationId());
        }
        if (esdLogSearch.getStatus() != null && !esdLogSearch.getStatus().isEmpty()){
            query.eq(EsdLog::getStatus, esdLogSearch.getStatus());
        }
        if (esdLogSearch.getLine()!= null && !esdLogSearch.getLine().isEmpty()) {
            query.like(EsdLog::getLineAndPosition, esdLogSearch.getLine());
        }
        if (esdLogSearch.getDeviceTypeName() != null){
            query.eq(EsdLog::getDeviceTypeName, esdLogSearch.getDeviceTypeName());
        }
        if (esdLogSearch.getChannelName() != null){
            query.eq(EsdLog::getChannelName, esdLogSearch.getChannelName());
        }
        if (esdLogSearch.getTimeScale() != null && esdLogSearch.getTimeScale().length == 2){
            query.between(EsdLog::getStartTime, esdLogSearch.getTimeScale()[0], esdLogSearch.getTimeScale()[1]);
        }
        query.orderByDesc(EsdLog::getStartTime);
        Page<EsdLog> esdLogPage = this.page(
                deviceLogPage ,query );
        return R.ok(new PageResult(esdLogPage.getTotal(),esdLogPage.getRecords()));
    }


    @Override
    public EsdLog getLatestLogByCreateTime(String stationId) {
        LambdaQueryWrapper<EsdLog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(EsdLog::getStationId, stationId);
        queryWrapper.orderByDesc(EsdLog::getStartTime);
        Page<EsdLog> logPage = new Page<>(1,1);
        Page<EsdLog> logPage1 = baseMapper.selectPage(logPage, queryWrapper);
        if (logPage1.getTotal() > 0) {
            return logPage1.getRecords().get(0);
        }else {
            return null;
        }
    }

    // TODO 在配置文件中配置日志文件的保存时间
    @Transactional
    @Override
    public void deleteEsdLog() {
        LambdaQueryWrapper<EsdLog> queryWrapper = new LambdaQueryWrapper<>();
        // 获取当前的年份和月份
        int year = LocalDateTime.now().getYear();
        int month = LocalDateTime.now().getMonthValue();
        if (month <= 6){
            year--;
            month += 6;
        }else {
            month -= 6;
        }
        // 使用YearMonth类创建本月的开始YearMonth对象
        LocalDate startDate = LocalDate.of(year, month , 1);
        // 使用LocalDateTime类从YearMonth对象获取本月的开始时间
        LocalDateTime startOfMonth = LocalDateTime.of(startDate, LocalDateTime.MIN.toLocalTime());
        queryWrapper.le(EsdLog::getStartTime , startOfMonth) ;
        queryWrapper.isNull(EsdLog::getEndTime) ;
        List<EsdLog> esdLogs = baseMapper.selectList(queryWrapper);
        esdLogs.forEach(esdLog -> {
            esdLog.setEndTime(LocalDateTime.now());
            baseMapper.updateById(esdLog) ;
        });
        try {
            TimeUnit.SECONDS.sleep(30);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        LambdaQueryWrapper<EsdLog> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.le(EsdLog::getStartTime , startOfMonth) ;
        baseMapper.delete(queryWrapper1);
    }

    @Override
    public List<EsdLog> getEsdLogByTime(Integer type , String status) {
        LambdaQueryWrapper<EsdLog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(EsdLog::getEnabled , true) ;
        queryWrapper.eq(EsdLog::getStatus , status);
        LocalDateTime start  ;
        LocalDateTime end  = LocalDateTime.now();
        // 当日日志
        if (type == 1){
            start = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        }else if (type == 2){ // 本周数据
            LocalDateTime now = LocalDateTime.now();
            start = now.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
                    .withHour(0)
                    .withMinute(0)
                    .withSecond(0);
        }else { // 本月数据
            LocalDate startDate = LocalDate.of(end.getYear(), end.getMonth() , 1);
            // 使用LocalDateTime类从YearMonth对象获取本月的开始时间
            start = LocalDateTime.of(startDate, LocalDateTime.MIN.toLocalTime());
        }
        queryWrapper.between(EsdLog::getStartTime, start, end);
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    public List<EsdLog> getEsdLogByTimeAndLine(Integer type, String status, String line) {
        LambdaQueryWrapper<EsdLog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(EsdLog::getEnabled , true) ;
        queryWrapper.eq(EsdLog::getStatus , status);
        queryWrapper.eq(EsdLog::getLine , line) ;
        LocalDateTime start  ;
        LocalDateTime end  = LocalDateTime.now();
        // 当日日志
        if (type == 1){
            start = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        }else if (type == 2){ // 本周数据
            LocalDateTime now = LocalDateTime.now();
            start = now.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
                    .withHour(0)
                    .withMinute(0)
                    .withSecond(0);
        }else { // 本月数据
            LocalDate startDate = LocalDate.of(end.getYear(), end.getMonth() , 1);
            // 使用LocalDateTime类从YearMonth对象获取本月的开始时间
            start = LocalDateTime.of(startDate, LocalDateTime.MIN.toLocalTime());
        }
        queryWrapper.between(EsdLog::getStartTime, start, end);
        return baseMapper.selectList(queryWrapper);
    }


    @Override
    public List<EsdLog> getEsdLogByTimeAndChanneType(Integer type, String status, String channelType) {
        LambdaQueryWrapper<EsdLog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(EsdLog::getEnabled , true) ;
        queryWrapper.eq(EsdLog::getStatus , status);
        queryWrapper.eq(EsdLog::getChannelName , channelType) ;
        LocalDateTime start  ;
        LocalDateTime end  = LocalDateTime.now();
        // 当日日志
        if (type == 1){
            start = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        }else if (type == 2){ // 本周数据
            LocalDateTime now = LocalDateTime.now();
            start = now.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
                    .withHour(0)
                    .withMinute(0)
                    .withSecond(0);
        }else { // 本月数据
            LocalDate startDate = LocalDate.of(end.getYear(), end.getMonth() , 1);
            // 使用LocalDateTime类从YearMonth对象获取本月的开始时间
            start = LocalDateTime.of(startDate, LocalDateTime.MIN.toLocalTime());
        }
        queryWrapper.between(EsdLog::getStartTime, start, end);
        return baseMapper.selectList(queryWrapper);
    }

    @Autowired
    private LineMapper lineMapper ;

    @Autowired
    private DeviceChannelMapper deviceChannelMapper ;

    @Override
    public Map<String, Object> searchData(AnalysisSearch analysisSearch) {
        Map<String, Object> result = new HashMap<>();
        Long lineId = analysisSearch.getLine() ;
        Date start = analysisSearch.getTimeScale()[0] ;
        Date end = analysisSearch.getTimeScale()[1] ;
        Line line = lineMapper.selectById(lineId);
        LambdaQueryWrapper<EsdLog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(EsdLog::getLine , line.getLineName()) ;
        queryWrapper.between(EsdLog::getStartTime, start, end);
        List<EsdLog> esdLogs = baseMapper.selectList(queryWrapper);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String timeStr = sdf.format(start) + " 至 " + sdf.format(end);
        result.put("timeStr", timeStr) ;
        result.put("line" , line.getLineName());

        Long count = deviceChannelMapper.countByLine(lineId);
        Long fanCount = deviceChannelMapper.countByLineAndChannelType(lineId , "离子风扇");
        Long tdCount = deviceChannelMapper.countByLineAndChannelType(lineId , "台垫接地");
        Long sbCount = deviceChannelMapper.countByLineAndChannelType(lineId , "设备接地");
        Long shCount = deviceChannelMapper.countByLineAndChannelType(lineId , "ESD手环");

        long fanErrorSeconds = 0L ;
        long tdErrorSeconds = 0L ;
        long sbErrorSeconds = 0L ;
        long shErrorSeconds = 0L ;

        long fanTotalSeconds = 0L ;
        long tdTotalSeconds = 0L ;
        long sbTotalSeconds = 0L ;
        long shTotalSeconds = 0L ;

        long totalSeconds = 0L ;
        long errorSeconds = 0L ;
        long totalCount = 0L ;
        long errorCount = 0L;

        Map<String , Integer> top5CountMap = new HashMap<>();
        Map<String , Long> top5TimeMap = new HashMap<>() ;

        for (EsdLog esdLog : esdLogs) {
            if (!esdLog.getStatus().equals("失联")) {
                totalCount++;
                Long tempTime;
                if (esdLog.getEndTime() != null) {
                    tempTime = esdLog.getEndTime().toEpochSecond(ZoneOffset.ofHours(8))
                            - esdLog.getStartTime().toEpochSecond(ZoneOffset.ofHours(8));
                } else {
                    tempTime = LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8))
                            - esdLog.getStartTime().toEpochSecond(ZoneOffset.ofHours(8));
                }
                totalSeconds = totalSeconds + tempTime;
                switch (esdLog.getChannelName()) {
                    case "离子风扇":
                        fanTotalSeconds = fanTotalSeconds + tempTime;
                        break;
                    case "台垫接地":
                        tdTotalSeconds = tdTotalSeconds + tempTime;
                        break;
                    case "设备接地":
                        sbTotalSeconds = sbTotalSeconds + tempTime;
                        break;
                    case "ESD手环":
                        shTotalSeconds = shTotalSeconds + tempTime;
                        break;
                }
                if (esdLog.getStatus().equals("异常")) {
                    errorCount++;
                    errorSeconds = errorSeconds + tempTime;
                    switch (esdLog.getChannelName()) {
                        case "离子风扇":
                            fanErrorSeconds = fanErrorSeconds + tempTime;
                            break;
                        case "台垫接地":
                            tdErrorSeconds = tdErrorSeconds + tempTime;
                            break;
                        case "设备接地":
                            sbErrorSeconds = sbErrorSeconds + tempTime;
                            break;
                        case "ESD手环":
                            shErrorSeconds = shErrorSeconds + tempTime;
                            break;
                    }
                    if(top5CountMap.containsKey(esdLog.getStationId())){
                        top5CountMap.put(esdLog.getLineAndPosition() + "  " + esdLog.getStationId(),
                                top5CountMap.get(esdLog.getLineAndPosition() + "  " + esdLog.getStationId()) + 1);
                    }else {
                        top5CountMap.put(esdLog.getLineAndPosition() + "  " + esdLog.getStationId(), 1);
                    }
                    if(top5TimeMap.containsKey(esdLog.getStationId())){
                        top5TimeMap.put(
                                esdLog.getLineAndPosition() + "  " + esdLog.getStationId(),
                                top5TimeMap.get(esdLog.getLineAndPosition() + "  " + esdLog.getStationId()) + tempTime);
                    }else {
                        top5TimeMap.put(esdLog.getLineAndPosition() + "  " + esdLog.getStationId(), tempTime);
                    }
                }
            }
        }
        String fanWdRate = "100%" ;
        if (fanTotalSeconds > 0) {
            fanWdRate = new BigDecimal(fanTotalSeconds - fanErrorSeconds)
                    .multiply(new BigDecimal(100))
                    .divide( new BigDecimal(fanTotalSeconds) ,2 ,RoundingMode.HALF_UP)
                     + "%";
        }
        String fanDetails = "数量: " + fanCount + "    比例: "
                + new BigDecimal(fanCount)
                .multiply(new BigDecimal(100))
                .divide( new BigDecimal(count) , 2 , RoundingMode.HALF_UP) + "%"
                + "    稳定性: " +  fanWdRate ;

        String tdWdRate = "100%" ;
        if (tdTotalSeconds > 0) {
            tdWdRate = new BigDecimal(tdTotalSeconds - tdErrorSeconds)
                    .multiply(new BigDecimal(100))
                    .divide( new BigDecimal(tdTotalSeconds) , 2 , RoundingMode.HALF_UP)+ "%";
        }
        String tdDetails = "数量: " + tdCount + "    比例: "
                + new BigDecimal(tdCount)
                .multiply(new BigDecimal(100))
                .divide( new BigDecimal(count) ,2 , RoundingMode.HALF_UP)+ "%"
                + "    稳定性: " + tdWdRate;

        String sbWdRate = "100%" ;
        if (sbTotalSeconds > 0) {
            sbWdRate = new BigDecimal(sbTotalSeconds - sbErrorSeconds)
                    .multiply(new BigDecimal(100))
                    .divide( new BigDecimal(sbTotalSeconds) ,2 , RoundingMode.HALF_UP)+ "%";
        }
        String sbDetails = "数量: " + sbCount + "    比例: "
                + new BigDecimal(sbCount)
                .multiply(new BigDecimal(100))
                .divide( new BigDecimal(count) , 2 , RoundingMode.HALF_UP)+ "%"
                + "    稳定性: " + sbWdRate;

        String shWdRate = "100%" ;
        if (shTotalSeconds > 0) {
            shWdRate = new BigDecimal(shTotalSeconds - shErrorSeconds)
                    .multiply(new BigDecimal(100))
                    .divide( new BigDecimal(shTotalSeconds) , 2 , RoundingMode.HALF_UP) + "%";
        }
        String shDetails = "数量: " + shCount + "    比例: "
                + new BigDecimal(shCount)
                .multiply(new BigDecimal(100))
                .divide( new BigDecimal(count) ,2 , RoundingMode.HALF_UP) + "%"
                + "    稳定性: " + shWdRate;

        result.put("fanDetails", fanDetails);
        result.put("shDetails", shDetails);
        result.put("tdDetails", tdDetails);
        result.put("sbDetails", sbDetails);

        result.put("errorCount", errorCount + "次");
        if(totalCount == 0){
            result.put("errorCountRate","0%");
        }else {
            result.put("errorCountRate", new BigDecimal(errorCount)
                    .multiply(new BigDecimal(100))
                    .divide(new BigDecimal(totalCount), 2, RoundingMode.HALF_UP) + "%");
        }
        result.put("errorTime", new BigDecimal(errorSeconds).divide(
                new BigDecimal(60) ,2 , RoundingMode.HALF_UP) + "分钟");
        if (totalSeconds == 0){
            result.put("errorTimeRate", "0%");
        }else {
            result.put("errorTimeRate", new BigDecimal(errorSeconds)
                    .multiply(new BigDecimal(100))
                    .divide( new BigDecimal(totalSeconds) ,2, RoundingMode.HALF_UP) + "%");
        }

        result.put("top5CountList" , getTop5(top5CountMap));
        result.put("top5TimeList" , getTop5_1(top5TimeMap)) ;
        return result;
    }

    @Value("${esd.reportTemplate}")
    private String reportTemplate ;
    @Override
    public void exportData(AnalysisSearch analysisSearch, HttpServletResponse response) throws IOException {
        Map<String, Object> result = searchData(analysisSearch);
        //基于POI在内存中创建一个Excel文件
        XSSFWorkbook excel = new XSSFWorkbook(Files.newInputStream(new File(reportTemplate).toPath()));
        XSSFSheet sheet = excel.getSheetAt(0);

        XSSFRow row = sheet.getRow(1);
        row.getCell(1).setCellValue((String) result.get("timeStr"));//日期
        row = sheet.getRow(2);
        row.getCell(1).setCellValue((String) result.get("line"));//日期

        row = sheet.getRow(4);
        row.getCell(1).setCellValue((String) result.get("fanDetails"));//离子风扇

        row = sheet.getRow(5);
        row.getCell(1).setCellValue((String) result.get("tdDetails"));//台垫接地

        row = sheet.getRow(6);
        row.getCell(1).setCellValue((String) result.get("shDetails"));//ESD手环

        row = sheet.getRow(7);
        row.getCell(1).setCellValue((String) result.get("sbDetails"));//设备接地

        row = sheet.getRow(9);
        row.getCell(1).setCellValue((String) result.get("errorTime"));
        row.getCell(3).setCellValue((String) result.get("errorTimeRate"));

        row = sheet.getRow(10);
        row.getCell(1).setCellValue((String) result.get("errorCount"));
        row.getCell(3).setCellValue((String) result.get("errorCountRate"));

        List<Map<String , Object>> top5CountList = (List<Map<String, Object>>) result.get("top5CountList");
        List<Map<String , Object>> top5TimeList = (List<Map<String, Object>>) result.get("top5TimeList");

        for (int i = 0; i < top5CountList.size(); i++) {
            row = sheet.getRow(14 + i);
            row.getCell(2).setCellValue((String) top5CountList.get(i).get("key"));
            row.getCell(3).setCellValue((Integer) top5CountList.get(i).get("value"));
        }

        for (int i = 0; i < top5TimeList.size(); i++) {
            row = sheet.getRow(14 + i);
            row.getCell(0).setCellValue((String) top5TimeList.get(i).get("key"));
            row.getCell(1).setCellValue((Long) top5TimeList.get(i).get("value"));
        }

        // 使用输出流进行表格下载,基于浏览器作为客户端下载
        OutputStream out = response.getOutputStream();
        response.setContentType("application/vnd.ms-excel");// 代表的是Excel文件类型
        response.setHeader("content-Disposition", "attachment;filename=report.xlsx");// 指定以附件形式进行下载
        excel.write(out);

        out.flush();
        out.close();
        excel.close();
    }

    private List<Map<String , Object>> getTop5(Map<String , Integer> map){
        int n = Math.min(map.keySet().size(), 5);
        List<Map.Entry<String, Integer>> list = new ArrayList<>(map.entrySet());
        List<Map<String , Object>> result = new ArrayList<>() ;
        for (int i = 0 ; i< n ; i++){
            int maxIndex = 0 ;
            for (int j = 0 ; j < list.size() ; j++){
                if (list.get(j).getValue() > list.get(maxIndex).getValue()){
                    maxIndex = j ;
                }
            }
            Map<String , Object> tempMap = new HashMap<>() ;
            tempMap.put("key", list.get(maxIndex).getKey());
            tempMap.put("value", list.get(maxIndex).getValue());
            result.add(tempMap) ;
            list.remove(maxIndex);
        }
        return result ;
    }

    private List<Map<String , Object>> getTop5_1(Map<String , Long> map){
        int n = Math.min(map.keySet().size(), 5);
        List<Map.Entry<String, Long>> list = new ArrayList<>(map.entrySet());
        List<Map<String , Object>> result = new ArrayList<>() ;
        for (int i = 0 ; i< n ; i++){
            int maxIndex = 0 ;
            for (int j = 0 ; j < list.size() ; j++){
                if (list.get(j).getValue() > list.get(maxIndex).getValue()){
                    maxIndex = j ;
                }
            }
            Map<String , Object> tempMap = new HashMap<>() ;
            tempMap.put("key", list.get(maxIndex).getKey());
            tempMap.put("value", list.get(maxIndex).getValue());
            result.add(tempMap) ;
            list.remove(maxIndex);
        }
        return result ;
    }
}
